/*
 * AVRX_EPROM.c
 *
 * Created: 12/9/2012 10:51:40 PM
 *  Author: Parag
 */ 

#ifndef	F_CPU
#define F_CPU 8000000UL
#endif

#include <avr/io.h>
#include "avrx/avrx-io.h"
#include "avrx/avrx-signal.h"
#include "avrx/avrx.h"
#include "avrx/serialio.h"       // From AvrX...
#include "avrx_hardware_custom.h"
#include "U0_LCD_Driver.h"

#include "U0_UART.h"
#include <stdbool.h>
#include <inttypes.h>
#include <ctype.h>
#include <stdio.h>

//global variables
char  g_buttonPressedFlag;
char g_buttonPressed; //up=4,down=3,left=2,right=1,center=0
#define BUTTON_CODE_UP 4
#define BUTTON_CODE_DOWN 3
#define BUTTON_CODE_CENTER 0

extern FILE uart_stream;
//# define BUFFER_SZE 100
#include <stdio.h>
typedef struct
{

	int size;
	int head;
	int count;

}Buffer;

char str[5];
char * StartGame = "STGM";
char * PlayMusic = "STMC";
char * SusMusic = "SPMC";
char * ResCap = "STCP";
char * ResSend = "STSD";
char * SusSend = "SPSD";

TimerControlBlock delay;
TimerControlBlock timerGame;
TimerControlBlock timerNote;
TimerControlBlock timerMusic;
TimerControlBlock adcDelay;
TimerControlBlock sendDelay;

bool GameOn, GameHang, MusicOn, MusicHang, CaptureOn, CaptureHang, SendOn, sendHang;

AVRX_MUTEX(timeOut);
AVRX_MUTEX(gameIn);
AVRX_MUTEX(capIn);
AVRX_MUTEX(sendIn);
AVRX_MUTEX(startup);

Buffer Bufferx;
uint16_t ADC_value_put, ADC_Value_get;


//prototypes
void BufferInit(Buffer * buf);
void SetupButtonInterrupts(void);
int HasButtonPressed();
int GetButtonPressed();
void SetupButtonInterrupts(void);
void SetupJoystickPins(void);
void MusicRest();
void MusicPlayA();
void MusicPlayB();
void MusicSetNote(int period_us);
void MusicSetupPort();
void MusicSetupTimer1();
void MusicPlayG();
void timerSetup();
void ADCSetup();
void BufferInit(Buffer * buf);
bool IsBufferFull(Buffer * buf);
bool isBufferEmpty(Buffer * buf);
void bufWrite(Buffer * buf,char ch);
char bufRead(Buffer*buf);

AVRX_SIGINT(AVRX_HARDWARE_TIMER_SIGNAL){
	
	IntProlog();                           // Switch to kernel stack/context
	AvrXTimerHandler();         // Call Time queue manager
	Epilog();                              // Return to tasks
}


AVRX_SIGINT(ADC_vect)
{
	IntProlog(); //interrupt prolog
	
	ADC_value_put = ADC;
	TIFR0 = (1<<OCF0A);

	Epilog();
}

AVRX_SIGINT(PCINT1_vect) {
	//remember this is called on pin change 0->1 and 1->0
	IntProlog(); //interrupt prolog
	//static uint8_t pinBPrev=1; //trace

	static unsigned char pressedPrev = 1;
	unsigned char pressed = (~PINB & (_BV(7)|_BV(6)|_BV(4))); //leaves a one on 7,6,4 if they are low because button is pressed

	//capture newly pressed buttons only
	if(pressed &  ~pressedPrev)
	{ //if button pressed and previously it wasn't

		if (g_buttonPressedFlag==0)
		{
			g_buttonPressedFlag=1;
			if (pressed & _BV(7))
			{
				g_buttonPressed=BUTTON_CODE_DOWN;
			} else if (pressed & _BV(6))
			{
				g_buttonPressed=BUTTON_CODE_UP;
			}else
			{// if (pressed & _BV(4)){
				g_buttonPressed=BUTTON_CODE_CENTER;
			}
			PORTB ^= (1<<0); //toggle led on B0
		}

	}
	else
	{
		//nothing to do
	}

	pressedPrev = pressed; //save pressed button status

	Epilog();

}

AVRX_GCC_TASKDEF(taskMusic,25,2){
	
	MusicSetupPort();
	MusicSetupTimer1();

	while(1)
	{
		
		MusicSetNote(2024);
		
		AvrXDelay(&timerNote, 500);
		
		MusicRest();
		
		MusicSetNote(2272);
		AvrXDelay(&timerNote, 500);
		MusicRest();
		
		MusicSetNote(2552);
		AvrXDelay(&timerNote, 500);
		MusicRest();
		
		AvrXDelay(&timerMusic, 500);
	}
}




AVRX_GCC_TASKDEF(storeADC, 20, 2)
{

	while(1)
	{
		AvrXDelay(&adcDelay, 2);		
		bufWrite(&Bufferx, (ADC_value_put >> 8));		// high
		bufWrite(&Bufferx, (ADC_value_put & 0x00ff)); // low		
		
	}
}





AVRX_GCC_TASKDEF(Deque, 20, 2)
{
	char temp;
	while(1)
	{
		AvrXDelay(&sendDelay, 2);

		temp = bufRead(&Bufferx);
		UARTPutch(temp,stdin);
		temp = bufRead(&Bufferx);
		UARTPutch(temp,stdin);
		
	}
}







AVRX_GCC_TASKDEF(taskGame,20,2){
	 //static volatile unsigned long ulDummyVariable = 3UL;

 static volatile unsigned char display[3] = {'0','0','0'};

 const int xDelay = 500;

     static enum state_t {open00,open0,open1,roll0,roll123,roll4,win,loss} state=open00;

     static unsigned char stopCount; //number of "wheels" stoped
	AvrXWaitSemaphore(&gameIn);
	
	LCD_Init();
	
     for( ;; )    {
		
		
        AvrXDelay(&timerGame, 2); //AvrXYield(); should have worked but did not compile
				

        switch (state) {
		case open00: //just an entry point
			
			state = open0;
			break;
		case open0:  //display play prompt
						LCD_WriteDigit('P',3);
						LCD_WriteDigit('L',4);
						LCD_WriteDigit('Y',5);
						state=open1;
						break;

        case open1: //wait for button press
                     // up causes exit to suspend,
                    // center causes game to start
					
					
							if (HasButtonPressed())
							{
				
								switch (GetButtonPressed()){
								case BUTTON_CODE_CENTER:
									state = roll0;
									break;
								case BUTTON_CODE_UP: //on up suspend and resume in same place
									GameHang = TRUE;
									LCD_Init();
									LCD_WriteDigit('1',4);
									LCD_WriteDigit('1',3);
									LCD_WriteDigit('3',2);
									LCD_WriteDigit('C',1);
									PORTB &= ~(1<<PB1);
									AvrXSuspend(AvrXSelf());
									LCD_Init();
									LCD_WriteDigit('P',3);
									LCD_WriteDigit('L',4);
									LCD_WriteDigit('Y',5);								
									state=open00;
									break;
								}
							}else{
								// do nothing if no button pressed
							}
							break;

        case roll0: //roll substate to init stop count
							stopCount = 0;
							state = roll123;
							break;

        case roll123: //roll state, takes three center presses to complete game
								if (HasButtonPressed()){
								   int i = GetButtonPressed();
									if(i == BUTTON_CODE_UP)
									{
										GameHang = TRUE;
										LCD_Init();
										LCD_WriteDigit('1',4);
										LCD_WriteDigit('1',3);
										LCD_WriteDigit('3',2);
										LCD_WriteDigit('C',1);
										PORTB &= ~(1<<PB1);
										AvrXSuspend(AvrXSelf());
										LCD_Init();
										LCD_WriteDigit(display[0],5);
										LCD_WriteDigit(display[1],4);
										LCD_WriteDigit(display[2],3);
										state = roll123;
										break;
									}					
									stopCount+=1;
									if (stopCount == 3){
										state = roll4;
									}
								}


								//generate and display roll of 3 random numbers
								if (stopCount < 3){
									display[0]='0' + rand()%5;
									LCD_WriteDigit(display[0],5);
									if (stopCount<2){
										display[1]='0' + rand()%5;
										LCD_WriteDigit(display[1],4);
										if (stopCount<1){
											display[2]='0' + rand()%5;
											LCD_WriteDigit(display[2],3);
										}
									}
								}

								AvrXDelay(&timerGame, 1000); //sets roll speed
								break;


        case roll4: //roll subsate t decide win or loss
						if (display[0]==display[1]&&display[0]==display[2]){
							state = win;
						}else{
							state = loss;
						}
						break;

        case win: // display win and restart
						AvrXDelay(&timerGame, xDelay); LCD_WriteDigit('W',3);
						AvrXDelay(&timerGame, xDelay); LCD_WriteDigit('I',4);
						AvrXDelay(&timerGame, xDelay); LCD_WriteDigit('N',5);
						AvrXDelay(&timerGame, xDelay); LCD_WriteDigit(display[0],3);
						AvrXDelay(&timerGame, xDelay); LCD_WriteDigit(display[1],4);
						AvrXDelay(&timerGame, xDelay); LCD_WriteDigit(display[2],5);
						AvrXDelay(&timerGame, xDelay);
						LCD_WriteDigit(';',3);  //clear
						LCD_WriteDigit('+',4);
						LCD_WriteDigit('5',5);
						//TODO: should show user cash
						state=open0;
						break;

        case loss: //display loss and restart
					AvrXDelay(&timerGame, xDelay);        LCD_WriteDigit('X',3);
					AvrXDelay(&timerGame, xDelay);        LCD_WriteDigit('X',4);
					AvrXDelay(&timerGame, xDelay);        LCD_WriteDigit('X',5);
					AvrXDelay(&timerGame, xDelay);        LCD_WriteDigit(display[0],3);
					AvrXDelay(&timerGame, xDelay);        LCD_WriteDigit(display[1],4);
					AvrXDelay(&timerGame, xDelay);        LCD_WriteDigit(display[2],5);
					AvrXDelay(&timerGame, xDelay);
					LCD_WriteDigit(';',3);  //clear
					LCD_WriteDigit('+',4);
					LCD_WriteDigit('0',5);
					state=open0;
					break;
        }
     }
 }
 
 
 AVRX_GCC_TASKDEF(menu,25,3)
 {
	 char c;
	 
	 while(1)
	 {
		 //polling just enough
		 AvrXDelay(&delay, 250);
		 c = UARTGetch(stdin);
		 if( c == 'S' )
		 {
			 str[0] = c;
			 str[1] = UARTGetch(stdin);
			 str[2] = UARTGetch(stdin);
			 str[3] = UARTGetch(stdin);
			 str[4] = '\0';
			 
			 
			 if(!strcmp(StartGame,str))
			 {
				// printf("\nstarting game\n");
				 if((GameHang) == TRUE && (GameOn == TRUE))
				 {
					 PORTB |= (1<<PB1);
					 GameHang = FALSE;
					 AvrXResume(PID(taskGame));					 
					 
				 }
				 else
				 {
					PORTB |= (1<<PB1);
					GameOn = TRUE;
					AvrXSetSemaphore(&gameIn);
					  
					 
				 }
			 }
			 else if(!strcmp(PlayMusic,str))
			 {
				// printf("\nplay music\n");
				 PORTB |= (1<<PB0);
				 AvrXRunTask(TCB(taskMusic));
			 }
			 else if(!strcmp(SusMusic,str))
			 {
				// printf("\nsuspending music\n");
				 PORTB &= ~(1<<PB0);
				 AvrXSuspend(PID(taskMusic));;
			 }
			 else if(!strcmp(ResCap,str))
			 {
				// printf("\nresuming capture\n\n");
				 PORTB |= (1<<PB2);
				 AvrXRunTask(TCB(storeADC));

			 }
			 else if(!strcmp(ResSend,str))
			 {
				// printf("\nresuming sending\n");
				 if(sendHang == TRUE && SendOn == TRUE)
				 {
					 sendHang = FALSE;
					 PORTB |= (1<<PB3);
						AvrXResume(PID(Deque));
					 
				 }
				 else
				 {
					 SendOn = TRUE;
					 PORTB |= (1<<PB3);
					 AvrXRunTask(TCB(Deque));
				
					 
				 }
			 }
			 else if(!strcmp(SusSend,str))
			 {
				// printf("\nsuspending send\n");
				 PORTB &= ~(1<<PB3);
				 sendHang = TRUE;
				AvrXSuspend(PID(Deque));
			 }
			 else
			 {
				 
			 }
			 
		 }
		 
		 
	 }
	 
 }
 
 
int main(void)
{
	stderr = stdout = stdin = &uart_stream;
	UARTInit();	/* Initialize Uart */
	
	GameOn = GameHang= MusicOn= MusicHang= CaptureOn= CaptureHang= SendOn= sendHang= FALSE;
	DDRB |= ((1<<PB2) | (1<<PB0) | (1<<PB1) | (1<<PB3));
	
	BufferInit(&Bufferx);
	ADCSetup();
	timerSetup();
	AvrXSetKernelStack(0);
	AvrXSetSemaphore (&EEPromMutex); //--- needed by AVRX  ****
	AVRX_HARDWARE_SETUP_COMMANDS;
	
	LCD_Init();
	
	LCD_WriteDigit('1',4);
	LCD_WriteDigit('1',3);
	LCD_WriteDigit('3',2);
	LCD_WriteDigit('C',1);
	

	SetupJoystickPins();
	SetupButtonInterrupts();
	
	AvrXRunTask(TCB(menu));
	AvrXRunTask(TCB(taskGame));
	
	Epilog();
	

	while(1)
	{
		
	}
	
}



void BufferInit(Buffer * buf)
{
	
	buf->size = 512;
	buf->head = 0;
	buf->count = 0;
}

bool IsBufferFull(Buffer * buf)
{
	
	//return((buf->tail + 412) % buf->size == buf->head);
	if(buf->count==100)
	{
		return TRUE;
	}
	else{
		return FALSE;
	}	
}

bool isBufferEmpty(Buffer * buf)
{	
	if (buf->count == 0)
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
	
}


void bufWrite(Buffer * buf,char ch)
{
	int tail = (buf->head + buf->count)% buf->size;
	
	if(IsBufferFull(buf))
	{
		PORTB &= ~(1<<PB2);
		AvrXTerminate(TCB(storeADC));	
	}

cli();
	AvrXWriteEEProm((void*) tail , ch);
	buf->head = (buf->head + 1)%buf->size;
	
	(buf->count)++;
	sei();
}

char bufRead(Buffer*buf)
{
	char temp;
	
	while(isBufferEmpty(buf))
	{
		AvrXDelay(&sendDelay, 4);
	}
	
	cli();
	temp = AvrXReadEEProm((void*)buf->head);
	buf->head = (buf->head  + 1)% buf->size;
	
	(buf->count)--;
	sei();

}

 void SetupButtonInterrupts(void)
 {
	 PCMSK1  |= (1<<PCINT15)+(1<<PCINT14)+(1<<PCINT12); //Unmask bit for Center Button and LR on Butterfly, PB4->PCINT12 to allow it to trigger interrupt flag PCIF1
	 EIMSK   = (1<<PCIE1);    //Enable interupt for flag PCIF1
	 
 }

int HasButtonPressed(){
	return g_buttonPressedFlag;
}

int GetButtonPressed(){
	int return_val;
	if (g_buttonPressedFlag==1){
		//save button THEN release hold
		return_val = g_buttonPressed;
		g_buttonPressedFlag=0;
		return return_val;
	}else{
		return -1;
	}

}

//setup pins for input
void SetupJoystickPins(void){
	//set as input and enable pull up resistor for pins 4,6,7 to use the pushbuttons
	DDRB  &= 0b00101111; //set three pindirections
	PORTB |= 0b11010000; //set three pinpullups
}


 
 
  void MusicSetupPort(){
	  #define PORTB5_MASK  0b00100000
	  DDRB |= PORTB5_MASK; //enable PORTB5 as output
  }

  void MusicSetNote(int period_us){
	  ICR1=period_us/2;
	  OCR1A=period_us/4;
	  TCNT1=0;
  }

  void MusicRest(){
	  OCR1A=0;
  };

  void MusicPlayG(){
	  MusicSetNote(2552);
	  AvrXDelay(&timerNote, 500);
	  MusicRest();
  }

  void MusicPlayA(){
	  MusicSetNote(2272);
	  AvrXDelay(&timerNote, 500);
	  MusicRest();
  }

  void MusicPlayB(){
	  MusicSetNote(2024);
	 AvrXDelay(&timerNote, 500);
	  MusicRest();
  }

  void MusicSetupTimer1() {
	  /* Timer/Counter1 Control Register A */
	  int value_TCCR1A_COM1A_2b = 2; //clear on match up, set on match down
	  int value_TCCR1A_COM1B_2b = 0; //disable
	  //this one is spread over both regsters
	  int value_TCCR1AB_WGM1_4b = 8; //PWM+frequency control using register ICR1
	  /* Timer/Counter1 Control Register B */
	  int value_TCCR1B_ICNC1_1b = 0; //no input capture noise cancel
	  int value_TCCR1B_ICES1_1b = 0; //Dont Care since not used
	  int value_TCCR1B_CS1_3b = 2; //use clock prescaler 8...see page 131 of datasheet...1Mhz for 8Mhz clk
	  TCCR1A = (value_TCCR1A_COM1A_2b << COM1A0) + (value_TCCR1A_COM1B_2b << COM1B0) + ((value_TCCR1AB_WGM1_4b&0x3) << WGM10);
	  TCCR1B = (value_TCCR1B_ICNC1_1b << ICNC1) + (value_TCCR1B_ICES1_1b << ICES1) + (((value_TCCR1AB_WGM1_4b&0b00001100)>>2) << WGM12) + (value_TCCR1B_CS1_3b<<CS10);
	  /* Timer/Counter1 Input Capture Register */
	  //also used for frequency setting
	  ICR1 = 0xFF/2; //16 bits //just some initial value
	  /* Timer/Counter 1 Interrupt Mask Register */
	  int value_TIMSK1_ICIE1_1b = 0;//input capture int. enable
	  int value_TIMSK1_OCIE1B_1b = 0;//output compare int. enable
	  int value_TIMSK1_OCIE1A_1b = 0;//output compare int. enable
	  int value_TIMSK1_TOIE1_1b = 0;//overflow int. enable
	  TIMSK1 = (value_TIMSK1_ICIE1_1b <<ICIE1) + (value_TIMSK1_OCIE1B_1b<<OCIE1B) + (value_TIMSK1_OCIE1A_1b<<OCIE1A) + (value_TIMSK1_TOIE1_1b<<TOIE1);
  }
  
  // CATCH BAD ISR (better than infinite resetting when developing)
  // bad ISR can cause infinite resetting (not good for EEPROM if you write to it)
  ISR(BADISR_vect){
	  static volatile char count;
	  LCD_Init();
	  LCD_WriteDigit('B',0);
	  LCD_WriteDigit('D',1);
	  LCD_WriteDigit('I',2);
	  LCD_WriteDigit('S',3);
	  LCD_WriteDigit('R',4);
	  while(1){
		  count++;
	  }
  }
  

  void ADCSetup(){
	cli();
	  DIDR0 = ~(0b11111011); 


		int refSel_2b=1;    //select avcc, change to 3 for 1.1V reference
		int adlar_1b=0;     //no need to left shift
		int muxSel_5b = 2;  //select ADC0 with single-ended conversion
		ADMUX = (refSel_2b << REFS0) + (adlar_1b<<ADLAR) + (muxSel_5b << MUX0);

		//enable adc  auto triger/ interrupt enable/ divider = 64
		ADCSRA = ((1<<ADEN) | (1<<ADATE) | (1 << ADIE) | (1 <<ADPS2) | (1<<ADPS1));

		ADCSRB = ((1<<ADTS1) | (1<<ADTS0)); // trigger = timer0 compare match
		sei();
  }
  
  void timerSetup()
  {
	  cli();
	  TCCR0A = ((1<<WGM01) | (1<<CS02) | (1<<CS00)); // prescale 1024 and ctc mode
	  OCR0A = 125; // 31.001 Khz
	  //TIMSK0 = (1<<OCIE1A);
	  sei();
  }
  
  
